

#include "CodeGen.h"

#include "contrib/CJsonObject.hpp"

#include "util.h"
#include "JsonClassGenerator.h"

#include "Symbols.h"
#include "rutil/FileSystem.hxx"
#include "rutil/Logger.hxx"

#include <fstream>
#include <regex>

using namespace codegen;
using namespace neb;
using namespace resip;
using namespace std;

#define RESIPROCATE_SUBSYSTEM LogSystem::LOGMODULE


PathInfo::PathInfo(const resip::Data& filepath)
{
    // separate path and file name
    static const std::regex re_pathname(R"String-literal(^(.*[/\\])?(.*)$)String-literal");
    //static const std::regex re_pathname(R"String-literal((.*[/\\])?(([_a-zA-Z]+).*)(\.(.*)))String-literal");
    std::smatch m;
    std::string s(filepath.c_str());
    if (std::regex_match(s, m, re_pathname))
    {
        path_ = m[1].matched ? m[1].str().c_str() : "./";

        // separate file name and extension
        s = m[2].str();
        static const std::regex re_extension(R"((.*)(\.(.*)))");
        if (std::regex_match(s, m, re_extension))
        {
            if (m[1].matched)
            {
                name_ = m[1].str().c_str();
                ext_full_ = m[2].str().c_str();
                ext_ = m[3].str().c_str();
            }
            else
            {
                name_ = m[3].str().c_str(); // file name like `.xxx` without extension
            }
        }
        else
        {
            name_ = s.c_str();  // file name without extension
        }

        // separate class name
        static const std::regex re_replace(R"([^_a-zA-Z])");
        class_name_ = std::regex_replace(name_.c_str(), re_replace, "_").c_str();
    }
}

GenConfig::GenType PathInfo::get_type() const
{
    return get_type(ext_);
}

codegen::GenConfig::GenType codegen::PathInfo::get_type(const resip::Data& ext)
{
    static const char* s_type_str[] = { "","json","xml" };
    if (ext == s_type_str[1])
    {
        return GenConfig::JSON;
    }
    else if (ext == s_type_str[2])
    {
        return GenConfig::XML;
    }
    else
    {
        return GenConfig::AUTO;
    }
}

//////////////////////////////////////////////////////////////////////////
GenConfig::GenConfig()
    : type_(GenConfig::AUTO)
    , out_file_ext_(".h")
    , use_file_value_(false)
    , enable_indent_(true)
    , reserve_tab_(false)
    , space_count_(4)
    
{
}

void GenConfig::set(const resip::Data& base_class_name /*= resip::Data::Empty*/,
    GenType type /*= GenConfig::AUTO*/,
    const resip::Data& out_file_ext /*= ".h"*/,
    bool use_file_value /*= false*/,
    bool enable_indent /*= true*/,
    bool reserve_tab /*= false*/,
    unsigned int space_count /*= 4*/)
{
    base_class_name_ = base_class_name;
    type_ = type;
    out_file_ext_ = out_file_ext;
    use_file_value_ = use_file_value;
    enable_indent_ = enable_indent;
    reserve_tab_ = reserve_tab;
    space_count_ = space_count;
}

//////////////////////////////////////////////////////////////////////////
void CodeGen::generate()
{
    int total = 0, failed = 0;
    ElapsedTime et;
    FileSystem::Directory pa(path_);
    if (pa.begin() == pa.end())
    {// file path
        InfoLog(<< "generate from file:" << path_);
        ElapsedTime et;
        if (!_generate(path_))
        {
            ++failed;
        }
        else
        {
            InfoLog(<< "elapsed time:" << et.elapsed());
        }
        ++total;
    }
    else
    {// directory path
        InfoLog(<< "generate from directory:" << path_);
        int i = 1;
        for (FileSystem::Directory::iterator it = pa.begin(); it != pa.end(); ++it)
        {
            if (it.is_directory())
            {
                continue;
            }

            resip::Data path(pa.getPath());
            if (!path.postfix("/") && !path.postfix("\\\\"))
            {
                path += "/";
            }

            InfoLog(<< i << " - generate from file:" << path + *it);
            ElapsedTime et;
            if (!_generate(path + *it))
            {
                ++failed;
            }
            else
            {
                InfoLog(<< i << " - elapsed time:" << et.elapsed());
            }
            ++total;
            ++i;
        }
    }

    cout << "total input file:" << total << ", generate failed:" << failed << endl;
    InfoLog(<< "total input file:" << total << ", generate failed:" << failed);
    cout << "Elapsed time:" << et.elapsed() << endl;
    InfoLog(<< "totle elapsed time:" << et.elapsed());
}

bool CodeGen::_generate(const resip::Data& in_filename)
{
    switch (GenConfig::instance().get_type())
    {
    case GenConfig::JSON:
        return _generate_json(in_filename);
        break;
    case GenConfig::XML:
        return _generate_xml(in_filename);
        break;
    default:
        return _generate_auto(in_filename);
        break;
    }
}

bool CodeGen::_generate_json(const resip::Data& in_filepath)
{
    resip::Data content;
    try
    {
        content = resip::Data::fromFile(in_filepath);
    }
    catch (const resip::BaseException&)
    {
        //cout << e.what() << endl;
        WarningLog(<< "file open failed - " << in_filepath);
        return false;
    }

    neb::CJsonObject* json = new neb::CJsonObject;
    if (!json->Parse(content))
    {
        WarningLog(<< " " << json->GetErrMsg());
        return false;
    }

    PathInfo pi(in_filepath);
    if (pi.class_name_.empty())
    {
        return false;
    }

    GenConfig& cfg = GenConfig::instance();
    IndentSetting::instance().setting(cfg.get_enable_indent(), cfg.get_reserve_tab(), cfg.get_space_count());
    JsonClassGenerator json_gen(json, pi.class_name_, cfg.get_base_class_name());
    if (json_gen.is_well_formed())
    {
        resip::Data out_file_path(pi.path_ + pi.name_ + cfg.get_out_file_ext());

        ofstream out(out_file_path.c_str());
        if (!out)
        {
            WarningLog(<< "file write failed - " << out_file_path);
            return false;
        }

        json_gen.encode(out);

        out.close();
        InfoLog(<< "generation done. " << in_filepath << " --> " << out_file_path);
    }
    else
    {
        WarningLog(<< json_gen.errors());
        return false;
    }

    return true;
}

bool CodeGen::_generate_xml(const resip::Data& in_filepath)
{
    return false;
}

bool CodeGen::_generate_auto(const resip::Data& in_filepath)
{
    PathInfo pi(in_filepath);
    GenConfig::GenType type = pi.get_type();
    if (type == GenConfig::JSON)
    {
        return _generate_json(in_filepath);
    }
    else if(type == GenConfig::XML)
    {
        return _generate_xml(in_filepath);
    }
    else
    {
        return (_generate_json(in_filepath) || _generate_xml(in_filepath));
    }
}






